const crypto = require('crypto');
var redis = require('../redisCluster');
var jwt = require('../jwt');
var _cookie = require('../cookie');
var config = require('../config');
const AES = require('../AES');
const RSA = require('../rsa');
// var Request = require('../apiModules/request');
// const $request = new Request();
const SSO = require('./sso');

module.exports = {
  expireTime: 60,//分钟
  redisTokenKey: 'a_t_k', //  access_token key 的简写
   accessTokensSuffix: '_accessToken', //  redis中的access token后缀
  refreshTokenSuffix: '_refreshToken',  //  redis中的refresh token后缀
  //登出清除用户登录信息
  logoutFun(req, res) {
    //清除客户端cookie
    _cookie.remove(res);
    _cookie.remove(res, this.redisTokenKey);

    // //  删除redis中的access token
    // let encryptedUserTokenKey = req.cookies[this.redisTokenKey];
    // const tokenKeyInCookie = encryptedUserTokenKey && RSA.decrypt(encryptedUserTokenKey);
    // const accessTokenKeyInRedis = `${tokenKeyInCookie}${this.accessTokensSuffix}`;
    // const refreshTokenKeyInRedis = `${tokenKeyInCookie}${this.refreshTokenSuffix}`;
    // redis.del(accessTokenKeyInRedis);
    // redis.del(refreshTokenKeyInRedis);

    //退出单点
    // res.json({
    //   status: 1,
    //   message: "user logout",
      // keys: {
      //   tokenKeyInCookie: tokenKeyInCookie,
      //   accessTokenKeyInRedis: accessTokenKeyInRedis,
      //   refreshTokenKeyInRedis: refreshTokenKeyInRedis
      // },
    //   path: config.domain + "/login",
    // })

    // // oss 登出跳转
    // SSO.logout(req.cookies.token || '');

    //设置用户登录状态失效
    // redis.del(this.getTokenKey(req.cookies.token || ""));

    //测试首次登录前删除redis缓存
    // redis.srem('APP_UPDATE_PASSWORD', 1);
    // redis.srem('APP_TERMS', 1);
  },
  /**
   * 删除登录过程的临时token
   */
  clearToken(res, _token, cookieName) {
    _cookie.remove(res, cookieName);
    redis.del(this.getTokenKey(_token));
  },
  //跳转登录页
  toLogin(req, res, redirect = false) {
    _cookie.remove(res, "vToken");
    _cookie.remove(res, "token");
    _cookie.remove(res, this.redisTokenKey);
    redis.del(this.getTokenKey(req.cookies.vToken || ""));
    if (redirect) {
      return res.redirect(307, config.domain + '/login');
    }
    else {
      let _lang = req.cookies.lang || req.headers.lang || req.language || "cn";
      res.json({
        status: -2,
        message: __("original.common_userlogin_outdatetorelogin", _lang),
        path: config.domain + "/login",
      });
    }
  },
  to403(req, res, body) {
    //记录日志
    res._resBody = {
      function: 'to403',
      errBody: body
    }
    res.render('403', {
      data: body
    });
  },

  /**
   * 将sso的jwt存至浏览器cookie
   * 将sso的jwt存至redis,用来实现单一设备登录限制功能
   * @param res 响应
   * @param tokenInfo token对象：{accessToken, refreshToken, expiresIn}
   * @param tokenKey
   * @param isSaveRefreshToken 是否需要保存refreshToken， 登录时需要保存，刷新token是不需要保存
   */
  saveJwt({res, tokenInfo, tokenKey, isSaveRefreshToken}){

    //  将token key存至redis, 并存放至cookie, 会话校验时，从cookie取出token key，再根据token key从redis取出token, 与cookie中的token比较
    redis.setex(`${tokenKey}${this.accessTokensSuffix}`, tokenInfo.expiresIn, tokenInfo.accessToken);
    //  将refresh token存至redis
    isSaveRefreshToken && redis.setex(`${tokenKey}${this.refreshTokenSuffix}`, 30 * 24 * 60 * 60, tokenInfo.refreshToken);

    // 将accessToken的key和value存入cookie，接口请求时的value作为登录状态判断依据
    const tokenVal = `${tokenKey}|&|${tokenInfo.accessToken}`

    _cookie.set(res, RSA.encrypt(tokenVal), this.redisTokenKey);
  },
  /**
   * 登录，设置cookie，token
   * @param {*} res 设置响应cookie
   * @param {*} cookieName 设置响应cookie的名称
   * @param {*} user 用户信息数据,包含是否唯一终端标识isUnique
   * @param {*} expire 失效时间，分钟
   * @param {*} newToken 是否创建新token
   */
  loginFun({ res, cookieName, user, expire }) {
    //清除登录token，生成新的登录状态token
    let tokenKey = this.setTokenKey(user);
    let _token = jwt.getToken({
      usrKey: tokenKey,
      usr: user.userId,
      flag: user.passwordFlag,
    }, '24h');
    let _tokenArr = _token.split('.');
    //缓存签发的token的第三段作为用户登录凭证
    redis.setex(tokenKey, 60 * expire, this.getTokenThirdPath(_token));
    //配置登录校验成功后的响应cookie
    _cookie.set(res, _token, cookieName);
  },
  //  从cookie中取出tokenKey并解密，如cookie没有tokenKey择重新生成
  getTokenFromCookie(req, user) {
    let userToken = []
    let encryptedUserTokenKey = req.cookies[this.redisTokenKey] || ''
    if (encryptedUserTokenKey) {
      try {
        const _token = RSA.decrypt(encryptedUserTokenKey)
        userToken = _token.split('|&|')
      } catch (err) {
        //记录日志
        res._resBody = {
          body: err,
          method: 'getTokenFromCookie'
        };
        userToken = ''
      }
    }
    return userToken
  },
  //生成toke key
  setTokenKey(user) {
    //唯一终端登录Key
    let _iKey_ = "BDH" + user.userId + "PC";
    //多终端登录，生成随机Key
    if (user.LOGIN_SESSION_LIMIT_FLAG === "0") {
      let rdm = crypto.randomBytes(Math.ceil(16)).toString('hex').toUpperCase();
      _iKey_ += rdm;
    }
    return _iKey_;
  },
  getTokenThirdPath(_token) {
    let _tokenArr = _token.split('.');
    return _tokenArr[_tokenArr.length - 1];
  },
  //获取token作为redis的key
  getTokenKey(_token) {
    let _iKey_ = "";
    try {
      let _user = jwt.verifyToken(_token);
      if (_user && _user.usrKey) {
        _iKey_ = _user.usrKey;
      }
    }
    catch (err) {
      _iKey_ = "";
    }
    return _iKey_;
  },
  //根据token作为key获取redis用户登录状态
  getLoginStatusByToken(_iKey_) {
    return new Promise((resolve, reject) => {
      redis.get(_iKey_, (status, result) => {
        if (status === 0) {
          resolve(result);
        }
        else {
          resolve(0);
        }
      });
    });
  },
  //根据用户id，语言获取用户信息
  getUser(userId = '', lang = 'cn') {
    const userKey = `portal:user:${userId}:${lang}`;
    return new Promise((resolve, reject) => {
      redis.hgetall(userKey).then((result) => {
        resolve(result);
      }).catch(() => {
        resolve(0);
      });
    });
  },
  //获取用户缓存权限集合
  getUserAuth(userId) {
    const userKey = `portal:user:uri:${userId}`;
    return new Promise((resolve, reject) => {
      redis.get(userKey, function (status, result) {
        if (status === 0) {
          resolve(JSON.parse(result).portalUriVoList);
        }
        else {
          resolve(0);
        }
      });
    });
  },
  //鉴权，校验用户是否合法进入登录二次校验
  checkLoginAuth(req) {
    return this.checkAuth(req.cookies.vToken || "", req.headers.lang || "cn");
  },
  //鉴权，校验用户是否合法登录
  async checkRouterAuth(req, res, setExpire = true) {
    //  以下是新的单点登录鉴权逻辑

    //  从cookie中取出token的key和value,并解密
    const userToken = this.getTokenFromCookie(req);

    // userToken不存在则表示未登录/登录状态失效
    if(!userToken) return '';

    const userTokenKey = userToken[0];
    const userTokenVal = userToken[1];

    //  从cookie中找到redis的key前缀, 再根据完整的key，从redis中取出access token,与cookie中的token比较
    //  这是为了判断是否是多端登陆，如果是同一账号多端登陆，那么token会不一样，redis中保存的是最新的一份token
    let tokenInRedis = await this.getLoginStatusByToken(`${userTokenKey}${this.accessTokensSuffix}`);

    // 校验token是否相同
    if (userTokenVal === tokenInRedis) {
      //  根据token获取userId
      const userInfo = await SSO.getUserId(tokenInRedis).catch(err => err);

      if (userInfo.userId) {
        req.headers.Authorization = `Bearer ${tokenInRedis}`;

        const lang = req.headers.lang || 'cn';
        //  根据userId获取用户信息
        let user = await this.getUser(userInfo.userId, lang);

        //  检查redis里的token有效期，是否需要刷新
        const leftTime = await redis.ttl(`${userTokenKey}${this.accessTokensSuffix}`).catch(err => err);

        //  剩余600秒是，刷新accessToken
        if(leftTime < 600){
          //  取出 refresh token
          const refreshToken = await this.getLoginStatusByToken(`${userTokenKey}${this.refreshTokenSuffix}`);
          if(!refreshToken) return '';

          //  根据 refresh token 换取新的 access token
          const newTokenInfo = await SSO.refreshToken(refreshToken).catch(err => err);

          if(!newTokenInfo.success) return '';

          await this.saveJwt({res, tokenInfo: newTokenInfo.data, tokenKey: userTokenKey, isSaveRefreshToken: false});
        } else {
          //  刷新cookie时间
          _cookie.set(res, req.cookies[this.redisTokenKey], this.redisTokenKey);
        }

        return user;
      }
    }
    return '';

  },
  //检查token有效性
  checkAuth(_token, lang, setExpire, res) {
    return new Promise(async (resolve, reject) => {
      let user = 0;
      try {
        //验证并解析token
        let tokenObj = jwt.verifyToken(_token);
        if (tokenObj) {
          let tokenThird = await this.getLoginStatusByToken(tokenObj.usrKey);
          //校验token第三段作为登录状态key
          if (this.getTokenThirdPath(_token) === tokenThird) {
            user = await this.getUser(tokenObj.usr, lang);
            //登录密码是否修改过
            if (tokenObj.flag === user.passwordFlag) {
              if (setExpire) {
                //更新用户登录状态有效时间
                redis.expire(tokenObj.usrKey, 60 * this.expireTime);
                //客户端cookie同步更新有效时间
                _cookie.set(res, _token);
              }
            }
          }
        }
        resolve(user);
      } catch (err) {
        resolve(user);
      }
    });
  },
  //页面跳转链接权限检查
  async checkUriAuth(user, routeRight) {
    let flag = false;
    // //共有资源
    // let commonPath = [
    //   "/workbench", //工作台
    //   "/workbench/myAccount", //工作台-我的账户
    //   "/workbench/message", //工作台-我的消息
    //   "/workbench/message/messageDetail", //工作台-我的消息明细
    //   "/workbench/news", //工作台-我的公告/新闻
    //   "/workbench/news/newsDetail", //工作台-我的公告/新闻明细
    // ]
    // //共有资源不做权限控制
    // if (routeRight.constructor === String
    //   && commonPath.indexOf(routeRight) >= 0) {
    //   return true;
    // }
    let userAuths = await this.getUserAuth(user.userId);
    if (userAuths && userAuths.length > 0) {
      let passAuths = userAuths.filter(uri => {
        // let reg = new RegExp("^(" + _uri + ")\/?");
        // return (reg.test(cpath));

        let _uri = uri ? uri.split("?")[0] : "";
        return (routeRight.constructor === Array && routeRight.indexOf(_uri) >= 0)
          || (routeRight === _uri);
      });
      if (passAuths && passAuths.length > 0) {
        flag = true;
      }
    }
    return flag;
  }
}
